test = {}
test.id = 2
test.version = 1.0
test.name = 'joy'
test.list = {}

function test:pr( ... )
	-- body
	print('tfboys',{...})
	return true
end

function test:ver( ... )
	-- body
	print('ver:' .. 1.0)
end

function show( ... )
	-- body
	print('show')
end



function mock(cls)
	-- body
	local mock_mgr = _G.mock_mgr or {}
	mock_mgr.fakers = mock_mgr.fakers or {}
	mock_mgr.backup = mock_mgr.backup or {}
	mock_mgr.glob_tab = mock_mgr.glob_tab or {}
	cls = (type(cls) == 'table' and cls) or mock_mgr.glob_tab
	for _,v in pairs(mock_mgr.fakers) do
		if getmetatable(getmetatable(v)) == cls then
			return v
		end
	end
	local compare_table = function (ta,tb)
		-- body
		--simple compare
		--TODO:支持嵌套table
		if type(ta) ~= type(tb) then return false end
		if ta == tb then return true end
		if #ta ~= #tb then return false end
		for k,v in pairs(ta) do
			if v ~= tb[k] then
				return false
			end
		end
		return true
	end
	local mock_obj = {
		last_method = nil,
		backup_method = nil,
	}

	mock_obj.when_call = function ( func )
		-- body
		mock_obj.need_mock_param = nil
		for _,obj in pairs({cls,_G}) do
			for k,v in pairs(obj) do
				if type(v) == 'function' and v == func then
					local meta = getmetatable(getmetatable(mock_obj))
					if meta ~= mock_mgr.glob_tab then
						if meta == obj then
							mock_obj.target_method = {name = k,ref = func}
							return mock_obj
						end
					else
						if obj == _G then
							mock_obj.target_method = {name = k,ref = func}
							return mock_obj
						end
					end
					
				end
			end
		end
	end

	mock_obj.then_answer = function ( ... )
		-- body
		local param = { ... }
		local method =  mock_obj.target_method.name
		local set_val = function ( ... ) 
			--body
			if mock_obj.need_mock_param then
				if compare_table({...},mock_obj.need_mock_param) then
					return unpack(param)
				else
					return mock_obj.backup_method( unpack(param) )
				end
			end
			return unpack(param)
		end
			
		local has_backup = false
		local has_replace = false
		if cls[method] and type(cls[method]) == 'function' then
			mock_mgr.backup[cls] = mock_mgr.backup[cls] or {}
			for _,v in pairs(mock_mgr.backup[cls]) do
				if v[1] == method then
					has_backup = true
					break
				end
			end
			if not has_backup then
				mock_obj.backup_method = cls[method]
				table.insert(mock_mgr.backup[cls],{method,cls[method]})
			end
			cls[method] = set_val 
			has_replace = true
		elseif  _G[method] and type(_G[method]) == 'function' then
			mock_mgr.backup[cls] = mock_mgr.backup[cls] or {}
			for _,v in pairs(mock_mgr.backup[cls]) do
				if v[1] == method then
					has_backup = true
					break
				end
			end
			if not has_backup then
				mock_obj.backup_method = _G[method]
				table.insert(mock_mgr.backup[cls],{method, _G[method]})
			end	
			_G[method] = set_val
			has_replace = true
		end
		if has_replace then
			mock_obj.last_method = method
			return mock_obj
		end
	end

	mock_obj.with_param = function ( ... )
		-- body
		mock_obj.need_mock_param = { ... }
		return mock_obj
	end

	local proxy = {}
	if cls == mock_mgr.glob_tab then
		proxy.__index = _G
	else
		proxy.__index = cls
	end
	setmetatable(mock_obj,proxy)
	setmetatable(proxy,cls)
	table.insert(mock_mgr.fakers,mock_obj)
	
	--unload mock module and rollback
	mock_mgr.unload = function ()
		-- body
		for _,v in pairs(mock_mgr.fakers) do
			local cls = getmetatable(getmetatable(v))
			local fun_list = mock_mgr.backup[cls]
			if fun_list then
				for _,v in pairs(fun_list) do
					if cls == mock_mgr.glob_tab then
						_G[v[1]] = v[2]
					else
						cls[v[1]] = v[2]
					end
				end
			end
		end
		for k,_ in pairs(mock_mgr) do
			mock_mgr[k] = nil
		end
		mock_mgr = nil

	end




	_G.mock_mgr = mock_mgr
	return mock_obj
end

--local mg = mock()
--mg.print('_G[print]')

print(show())
mock().when_call(show).then_answer('ding!!')
print(show(1))
print(show())
--mock_mgr.unload()
print(show())
test.pr()
--mock(test).when(test.pr).then_answer(2,3)
local mo = mock(test)
mo.when_call(test.pr).then_answer('12')
mo:ver()
print(test.pr())
print(show())
mock_mgr.unload()
print(show())
print(test.pr())
mock().when_call(show).with_param(999).then_answer(886,1314)
--table.foreach(debug.getinfo(show),print)
print(show())
print(show(999))
print(show(999))
print(show(9991))
mock().when_call(show).then_answer('diuni~~~')
print(show())
